<?php


use entity\ApiEntity;

/**
 * Class TplAction
 * @link https://www.php.net/manual/zh/class.yaf-action-abstract.php
 * @method bool display (string $tpl, array $parameters = []) : bool
 * @method \Yaf_View_Interface getView () : Yaf_View_Interface
 * @method \Yaf_Request_Abstract getRequest () : Yaf_Request_Abstract
 * @method \Yaf_Response_Abstract getResponse () : Yaf_Response_Abstract
 * @package lib\yaf
 */
abstract class CYafTplAction extends \Yaf_Action_Abstract
{

    const DISPLAY_TPL = true;
    const DISPLAY_ECHARTS = false;

    public $js = [
        'common.js'
    ];
    protected $echartsJs = [
        'echarts/echarts.min.js',
        'echarts/theme/dark.js',
        'echarts/theme/macarons.js',
    ];
    public $css = [];

    final public function execute()
    {
        $method = $this->getRequest()->getMethod();

        switch ($method) {
            case 'POST':
            case 'PUT':
                $this->executeApi();
                break;
            default:
                $this->executeTpl();
                break;
        }
    }

    abstract protected function main();

    protected function post(ApiEntity &$api)
    {
    }

    protected function put(ApiEntity &$api)
    {
    }

    private function auth()
    {
        $white = ['login'];

        $controller = strtolower($this->getRequest()->getControllerName());

        if (in_array($controller, $white)) return;

        if (\Yaf_Session::getInstance()->get('uid') == 0) throw new \lib\exception\ReadableException('未登录', self::API_CODE_ERROR_UNKNOWN_USER);

    }

    private function executeApi()
    {
        $api = new ApiEntity;
        $api->code = self::API_CODE_SUCCESS;
        $id = $this->getPost('_unique_id');

        \modules\tracing\zipkin\IdMaker::setId($id);

        $tracing = ZipkinTracing::start(ZipkinTracing::SERVERNAME_WEB_VIEW);

        try {

            $tracing->span(ZipkinTracing::SPAN_WEB_EXECUTE, [
                ['request', $this->getController()->getRequest()],
            ]);

            $start_time = microtime(true);

            $this->auth();

            $method = strtolower($this->getRequest()->getMethod());
            $this->$method($api);

            $runtime = microtime(true) - $start_time;

            if ($runtime > 0.2) {
                //todo 报警
            }

        } catch (\lib\exception\ReadableException $e) {

            $api->code = $e->getCode();
            $api->msg = $e->getMessage();

        } catch (\lib\exception\Interact $e) {

            $api->code = $e->getCode() ?: self::API_CODE_INTERACT;
            $api->msg = $e->getMessage();

        } catch (\Exception $e) {

            $api->code = self::API_CODE_SYSTEM_ERROR;
            $api->msg = '系统错误,' . $e->getMessage();

        } finally {

            $this->getResponse()->setBody(json_encode($api, JSON_UNESCAPED_UNICODE));

            $tracing->finishSpan();
        }
    }

    private function executeTpl()
    {
        $path = substr(static::class, 0, -6);

        $path = strtolower($path);
        $controller = strtolower($this->getRequest()->getControllerName());



        $success = false;
        $message = '';
        try {
            $this->main();
            $success = true;
        } catch (\Exception $e) {
            $message = $e->getMessage();
        } finally {

            if (!static::DISPLAY_TPL) return;

            if (!file_exists(VIEW_PATH . '/' . $controller . '/' . $path . '.phtml')) throw new \Exception('Failed opening template');

            if ($success) {
                if (static::DISPLAY_ECHARTS) $this->js = array_merge($this->js, $this->echartsJs);

                $this->assign('_js', array_map(function ($v) {
                    return "/js/" . $v . "?v=" . __VERSION__;
                }, $this->js));

                $this->assign('_css', $this->css);

                $this->display('../common/header');
                $this->display($path);
                $this->display('../common/footer');
            } else {

                $this->assign('message', $message);

                $this->display('../error/error');
            }
        }
    }

    public function getPut($name = null, $default = null)
    {
        $_PUT = [];
        if ('put' == strtolower($_SERVER['REQUEST_METHOD'])) {
            parse_str(file_get_contents('php://input'), $_PUT);
        }
        return $request = is_null($name) ? $_PUT : $_PUT[$name] ?? $default;
    }

    public function getPost($name = null, $default = null)
    {
        return $request = is_null($name) ? $this->getRequest()->getPost() : $this->getRequest()->getPost($name, $default);
    }

    public function getQuery($name = null, $default = null)
    {
        return $request = is_null($name) ? $this->getRequest()->getQuery() : $this->getRequest()->getQuery($name, $default);
    }

    public function assign($name, $value = null, $safe = true)
    {
        $loop = is_array($name) ? $name : [$name => $value];

        foreach ($loop as $k => $v) {
            if (is_string($v) && $safe) $v = htmlentities($v);
            if (is_array($v) && $safe) $v = $this->htmlentities($v);
            $this->getView()->assign($k, $v);
        }
    }

    protected function js($path)
    {
        $js = is_string($path) ? [$path] : $path;

        $this->js = array_merge($js, $this->js);
    }

    protected function css($path)
    {
        $this->css = array_map(function ($v) {
            return "/css/" . $v . "?v=" . __VERSION__;
        }, is_string($path) ? [$path] : $path);
    }

    public function htmlentities($value)
    {
        if (is_string($value)) return htmlentities($value);
        if (is_array($value)) {
            foreach ($value as &$v) {
                $this->htmlentities($v);
            }
        }
        return $value;
    }

}
